package com.tianji.common;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.StragyHttp.utils.RedisKit;
import com.xxl.job.core.biz.model.ReturnT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Description: 分页查询数据后进行更新
 *
 * @author chenjia
 * @version 1.0
 * @date 2024/12/25 10:01
 */
@Slf4j
public class PageSelect {
    public ReturnT<String> authorizerAccessTokenHandler(String param) {
        //1.查询待处理用户数据
        LocalDateTime now = LocalDateTime.now();
        //查询半小时后的时间
        LocalDateTime halfHourLater = now.plusMinutes(30);
        //查询token有效期小于半小时后时间的数据，或token为空的数据
        LambdaQueryWrapper<MiniappInfo> queryWrapper = new QueryWrapper<MiniappInfo>().lambda()
                .and(q -> q.isNull(MiniappInfo::getAuthorizerAccessToken).or().lt(MiniappInfo::getAccessExpiresIn, halfHourLater));
        long c = 1;
        Page<MiniappInfo> page = new Page<>();
        page.setSize(1000);
        Seller seller;
        while (true){
            page.setCurrent(c);
            page = miniappInfoService.page(page,queryWrapper);
            if(CollUtil.isEmpty(page.getRecords())){
                break;
            }
            //待更新列表
            List<MiniappInfo> upList = new ArrayList<>();
            for(MiniappInfo s : page.getRecords()) {
                try {
                    seller = sellerService.getSellerFromCache(s.getSellerId());
                    if (seller == null || !seller.getDistabled()) {
                        continue;
                    }
                    //第三方小程序授权码
                    String componentAccessToken = DyHdUtil.getComponentAccessToken(1);
                    log.info("[处理小程序token任务]第三方小程序授权码:{}", componentAccessToken);
                    AccessTokenResp tokenResp = null;
                    //1.判断刷新有效期是否过期
                    //未过期
                    if (s.getRefreshExpiresIn() != null && s.getRefreshExpiresIn().isAfter(now)) {
                        tokenResp = DyOpenApiUtil.authorizerRefreshToken(componentAccessToken, s.getAuthorizerRefreshToken());
                        //已过期，重新获取授权码，并获取授权信息
                    } else {
                        //获取授权码
                        AuthRetrieveCodeResp codeResp = DyOpenApiUtil.retrieveAuthorizationCode(componentAccessToken, s.getAppId());
                        if (codeResp != null && StrUtil.isNotBlank(codeResp.getAuthorization_code())) {
                            //获取授权信息
                            tokenResp = DyOpenApiUtil.getAuthorizerAccessToken(componentAccessToken, codeResp.getAuthorization_code());
                        }
                    }
                    if (tokenResp != null) {
                        MiniappInfo up = new MiniappInfo().setId(s.getId()).setAuthorizerAccessToken(tokenResp.getAuthorizer_access_token()).setAuthorizerRefreshToken(tokenResp.getAuthorizer_refresh_token());
                        up.setAccessExpiresIn(LocalDateTime.now().plusSeconds(tokenResp.getExpires_in()));
                        up.setRefreshExpiresIn(LocalDateTime.now().plusSeconds(tokenResp.getRefresh_expires_in()));
                        upList.add(up);
                    }
                } catch (Exception e) {
                    log.error("[处理小程序token任务]异常小程序:" + s.getAppId(), e);
                }
            }
            if(CollUtil.isNotEmpty(upList)) {
                miniappInfoService.updateBatchById(upList);
                log.info("[处理小程序token任务]更新成功条数:{}", upList.size());
            }
            c++;
        }
        return ReturnT.SUCCESS;
    }

    /**@Description: 多线程加锁处理
     * @param
     * @return
     * @version v1.0
     * @author jia
     * @date 2024/12/25 10:27
     */
    public void activityOrderHandler() {
        // 下单有礼任务 查询活动时间,结束时间近一个月内的
        List<PointActivity> pointActivityList = pointActivityService.list(new QueryWrapper<PointActivity>()
                .lambda()
                .eq(PointActivity::getSubType, PointsActivityEnum.ORDER.getIndex())
                .ge(PointActivity::getEndTime, LocalDateTime.now().minusMonths(1L)));
        if (CollectionUtils.isEmpty(pointActivityList)) {
            logger.info("[下单有礼订单校验任务],没有符合条件的活动");
            return;
        }
        logger.info("[下单有礼订单校验任务],需要处理的活动数量:{}", pointActivityList.size());
        ExecutorService executorService = ExecutorBuilder.create().setWorkQueue(new LinkedBlockingQueue<>())
                .setCorePoolSize(20).setMaxPoolSize(20).build();
        for (PointActivity pointActivity : pointActivityList) {
            executorService.execute(() -> {
                // 设置锁
                String lock = RedisKey.ACTIVITY_ORDER_HANDLER_JOB + pointActivity.getId();
                try {
                    if (!RedisKit.lock(lock, 10, TimeUnit.MINUTES)) {
                        log.error("[下单有礼订单校验任务] 商家:{},活动id:{},获取锁失败,不执行任务", pointActivity.getShopName(), pointActivity.getId());
                        return;
                    }
                    Long sellerId = pointActivity.getSellerId();
                    Long pointActivityId = pointActivity.getId();
                    Seller seller = sellerService.getSellerFromCache(sellerId);
                    //查询参与记录
                    //优先检查状态为已通过,待发放的记录
                    ShardingGetListDTO shardingGetListDTO = new ShardingGetListDTO();
                    shardingGetListDTO.setSellerId(sellerId);
                    shardingGetListDTO.setShardingTableEnum(ShardingTableEnum.POINT_ACTIVITY_RECORD);
                    QueryWrapper<PointActivityRecord> passWrapper = new QueryWrapper<>();
                    passWrapper.lambda()
                            .eq(PointActivityRecord::getActivityId, pointActivityId)
                            .and(qw -> qw.eq(PointActivityRecord::getIsPass, true).or()
                                    .eq(PointActivityRecord::getStatus, PrizeStatusEnum.NOT_GIVE.getIndex()));
                    List<PointActivityRecord> pointActivityRecordList =
                            pointActivityRecordService.shardingGetList(shardingGetListDTO, passWrapper);
                    if (CollectionUtils.isEmpty(pointActivityRecordList)) {
                        logger.info("[下单有礼订单校验任务] 商家:{},活动id:{},没有需要处理待发放的记录", pointActivity.getShopName(), pointActivityId);
                    } else {
                        // 开始处理 -- 待发放校验
                        // 如果不符合条件,则返还库存,并将状态置为已失效
                        checkPass(pointActivity, seller, pointActivityRecordList);
                    }
                    //处理未通过的记录
                    QueryWrapper<PointActivityRecord> notPassWrapper = new QueryWrapper<>();
                    notPassWrapper.lambda()
                            .eq(PointActivityRecord::getActivityId, pointActivityId)
                            .and(qw -> qw.eq(PointActivityRecord::getIsPass, false).or()
                                    .eq(PointActivityRecord::getStatus, PrizeStatusEnum.GIVE_FALSE.getIndex()));
                    pointActivityRecordList = pointActivityRecordService.shardingGetList(shardingGetListDTO, notPassWrapper);
                    if (CollectionUtils.isEmpty(pointActivityRecordList)) {
                        logger.info("[下单有礼订单校验任务] 商家:{},活动id:{},没有需要处理未通过的记录", pointActivity.getShopName(), pointActivityId);
                    } else {
                        // 开始处理 -- 未通过校验
                        checkPass(pointActivity, seller, pointActivityRecordList);
                    }
                } catch (Exception e) {
                    log.error("[下单有礼订单校验任务] 商家:{},活动id:{},处理下单有礼订单任务异常", pointActivity.getShopName(), pointActivity.getId(), e);
                } finally {
                    RedisKit.releaseLock(lock);
                }
            });
        }
        executorService.shutdown();
    }

    /**@Description: 获取凭证缓存和分布式锁的运用
     * @param
     * @return
     * @version v1.0
     * @author jia
     * @date 2024/12/25 14:42
     */
    public static AccessToken getAccessToken(String appKey,String appSecret,String shopId) {
        //先从缓存中拿
        String key = RedisKey.DOU_DIAN_ACCESS_TOKEN_KEY + appKey + ":" + shopId;
        String token = RedisKit.getStr(key);
        if (StrUtil.isNotBlank(token)) {
            GlobalConfig.AddAppKeyAndAppSecret(appKey, appSecret);
            return AccessTokenBuilder.parse(token, appKey);
        }
        String lockKey = RedisKey.DOU_DIAN_ACCESS_TOKEN_KEY + "lock";
        int count = 1;
        try {
            //循环获取锁
            while (!RedisKit.lock(lockKey, 5)) {
                //超过5次都未获取到锁，则抛出异常
                if (count > 5) {
                    throw new ServiceException("获取AccessToken失败，请稍后再试");
                }
                //等待200ms
                ThreadUtil.sleep(200L);
                count++;
            }
            //重新获取
            token = RedisKit.getStr(key);
            if (StrUtil.isNotBlank(token)) {
                GlobalConfig.AddAppKeyAndAppSecret(appKey, appSecret);
                return AccessTokenBuilder.parse(token, appKey);
            }
            //没有则重新获取
            //超时时间
            GlobalConfig.initHttpClientReadTimeout(12000);
            //建连时间
            GlobalConfig.initHttpClientConnectTimeout(3000);
            //设置appKey和appSecret，全局设置一次
            GlobalConfig.initAppKey(appKey);
            GlobalConfig.initAppSecret(appSecret);
            //小程序应用，应传送authid(授权管理中的shopId)和type
            AccessToken accessToken = AccessTokenBuilder.build(shopId, "MiniApp");
            log.info("[获取AccessToken] accessToken:{}", JSON.toJSONString(accessToken));
            log.info("[获取AccessToken] token:{},shop_id:{},expireIn:{}", accessToken.getAccessToken(), shopId, accessToken.getExpireIn());
            RedisKit.set(key, accessToken.getAccessToken(), accessToken.getExpireIn(), TimeUnit.SECONDS);
            return accessToken;
        } catch (Exception e) {
            log.info("[获取AccessToken],异常,shop_id:{},异常信息:{}", shopId, ExceptionUtil.stacktraceToString(e));
        } finally {
            RedisKit.releaseLock(lockKey);
        }
        return null;
    }

}
